﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GameEngine.PDNParser;
using System.IO;

namespace PDNTests
{
    /// <summary>
    /// Raw PDN Testing Library
    /// </summary>
    public static class RawPDNTest
    {
        /// <summary>
        /// TestPDN Path
        /// </summary>
        private const string path = "c:\\TestPDN";

        /// <summary>
        /// Directory for testing writing PDN format
        /// </summary>
        private const string _outputPath = "c:\\WritingPDNTest";

        private const string _errorPath = "c:\\ErrorPDNs";

        private static int _fileIndex = 0;
        private static int _errorIndex = 0;
       
        /// <summary>
        /// Test method for raw parsing one PDN file
        /// </summary>
        public static PDNRawGameList ParseFileRaw(string fileName, bool testWriting = true, bool testInterpretation = true)
        {
            PDNRawGameList rawGameList;

            try
            {
                using(FileStream stream = new FileStream(fileName, FileMode.Open))
                {
                    PDNReader reader = new PDNReader();
                    if(reader.ParseFile(stream, out rawGameList) == false)
                    {
                        Console.WriteLine("Error parsing: " + reader.ErrorMessage + " in file:" + fileName);
                        return null;
                    }

                    stream.Close();
                }

                Console.WriteLine("File '" + fileName + "' has been successfully parsed.");

                if (testWriting)
                {
                    // test writing
                    if (TestPDNWriting(rawGameList) == false)
                        return null;
                }


                if(testInterpretation)
                {
                    // if requested, test PDN game interpretation

                    //if (TestInterpretingGames(rawGameList) == false)
                    //    return null;

                    TestInterpretingGames(rawGameList);
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                return null;
            }

            return rawGameList;
        }

        /// <summary>
        /// Tests writing
        /// </summary>
        private static bool TestPDNWriting(PDNRawGameList pdnGameList)
        {
            // create directory, if necessary
            if (Directory.Exists(_outputPath) == false)
                Directory.CreateDirectory(_outputPath);

            // create the name of file
            string filePath = Path.Combine(_outputPath, string.Format("outputPDN_{0}.pdn", _fileIndex));
            _fileIndex++;

            try
            {
                Console.WriteLine("Writing PDN File...");

                // do writing
                using (FileStream stream = new FileStream(filePath, FileMode.Create))
                {
                    PDNWriter writer = new PDNWriter();
                    if(writer.WriteGameList(stream, pdnGameList) == false)
                    {
                        Console.WriteLine("Error writing PDN: " + writer.Error);
                        stream.Close();
                        return false;
                    }

                    stream.Close();
                }

                Console.WriteLine("File has been successfully written.");
            }
            catch(Exception ex)
            {
                Console.WriteLine("Exception in writing PDN: " + ex.Message);
                return false;
            }

            return true;
        }

        private static void WriteErrorPDN(PDNRawGame game)
        {
            // create error path for PDN files
            if (Directory.Exists(_errorPath) == false)
                Directory.CreateDirectory(_errorPath);

            // create the name of file
            string filePath = Path.Combine(_errorPath, string.Format("errorPDN_{0}.pdn", _errorIndex));
            _errorIndex++;

            try
            {
                Console.WriteLine("Writing Error PDN File...");
                
                PDNRawGameList tmpGameList = new PDNRawGameList();
                tmpGameList.Games.Add(game);

                // do writing
                using (FileStream stream = new FileStream(filePath, FileMode.Create))
                {
                    PDNWriter writer = new PDNWriter();
                    if (writer.WriteGameList(stream, tmpGameList) == false)
                    {
                        Console.WriteLine("Error writing PDN: " + writer.Error);
                        stream.Close();
                    }

                    stream.Close();
                }

                Console.WriteLine("Error File has been successfully written.");
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception in writing error PDN: " + ex.Message);
            }
        }

        /// <summary>
        /// Tests interpretation of loaded PDN library
        /// </summary>
        private static bool TestInterpretingGames(PDNRawGameList gameList)
        {
            try
            {
                PDNInterpreter interpreter = new PDNInterpreter();

                // iterate all games
                for(int i = 0; i < gameList.Games.Count; i++)
                {
                    Console.WriteLine(string.Format("Interpreting game: {0}/{1}...", i + 1, gameList.Games.Count));
                    try
                    {
                        interpreter.MakeCompleteInterpretation(gameList.Games[i]);
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine("Interpretation failed - Exception: " + ex.Message);

                        // write error PDN
                        WriteErrorPDN(gameList.Games[i]);
                    }
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine("Global Interpretation failed - Exception: " + ex.Message);
                return false;
            }

            return true;
        }


        /// <summary>
        /// The main public method for raw test parsing
        /// </summary>
        public static void DoPDNRawTest()
        {
            // clean up written files
            if(Directory.Exists(_outputPath))
            {
                string[] files = Directory.GetFiles(_outputPath);
                foreach(var file in files)
                {
                    try
                    {
                        File.Delete(file);
                    }
                    catch
                    { }
                }
            }

            // clean up written files
            if (Directory.Exists(_errorPath))
            {
                string[] files = Directory.GetFiles(_errorPath);
                foreach (var file in files)
                {
                    try
                    {
                        File.Delete(file);
                    }
                    catch
                    { }
                }
            }

            
            // parse and write PDN files
            if (DoRecursiveParsing(path))
            {
                Console.WriteLine("Re-reading already written files...");

                // reparse already written PDN files
                if(DoRecursiveParsing(_outputPath, false, false))
                    Console.WriteLine("Test passed.");
                else
                    Console.WriteLine("Test failed.");
            }
            else
                Console.WriteLine("Test failed.");
        }

        /// <summary>
        /// Recursive method for parsing test PDN directory
        /// </summary>
        private static bool DoRecursiveParsing(string sPath, bool testWriting = true, bool doInterpreting = true)
        {
            // parse all files
            string[] files = Directory.GetFiles(sPath, "*.pdn");

            foreach(string file in files)
            {
                if (ParseFileRaw(file, testWriting, doInterpreting) == null)
                    return false;
            }

            // go deeper into the subdirectiries
            string[] directories = Directory.GetDirectories(sPath);

            foreach(string directory in directories)
            {
                if (DoRecursiveParsing(directory) == false)
                    return false;
            }

            // return success
            return true;
        }      
    }
}
